# Loading packages
library(pryr) # Memory usage information
library(lubridate) # date manipulation
Attaching package: ‘lubridate’
The following object is masked from ‘package:base’:
date
library(dplyr) # data manipulation
Attaching package: ‘dplyr’
The following objects are masked from ‘package:lubridate’:
intersect, setdiff, union
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
library(tidyr) # data manipulation
library(ggplot2) # graphics
library(plotly)
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
library(stringr)
library(stringi) # remove diacritics
library(ggfortify)
library(rjson)
library(nasapower)
There are 4 csv files containing data about the fires of all ocurrences around all brazilian territory. Each file has data between 1st Sept to 30th Sept of each year from 2015 to 2019.
temp <- list.files(path = "./database" ,pattern = "*.csv")
myfiles = lapply(paste0("./database/",temp), read.csv)
nrecords <- lapply(myfiles, nrow)
data <- bind_rows(myfiles)
Unequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorUnequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorUnequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vector
names(data)
[1] "datahora" "satelite" "pais" "estado" "municipio" "bioma"
[7] "diasemchuva" "precipitacao" "riscofogo" "latitude" "longitude" "frp"
# As we have many data records, we'll be working with a small subset of it
df <- data[sample(nrow(data), 0.1*nrow(data)), ]
totalNo <- nrow(df)
object_size(df)
8.32 MB
there are 820929 observations in our dataset which sums up to 66.8 MB of storaged data.
head(df)
str(df)
'data.frame': 88511 obs. of 12 variables:
$ datahora : chr "2018/09/07 17:45:00" "2017/08/31 16:34:00" "2017/09/06 17:35:00" "2017/09/24 17:24:00" ...
$ satelite : Factor w/ 1 level "AQUA_M-T": 1 1 1 1 1 1 1 1 1 1 ...
$ pais : Factor w/ 1 level "Brasil": 1 1 1 1 1 1 1 1 1 1 ...
$ estado : chr "RONDONIA" "MARANHAO" "MATO GROSSO" "PARA" ...
$ municipio : chr "MONTE NEGRO" "CAROLINA" "NOVA BANDEIRANTES" "PLACAS" ...
$ bioma : Factor w/ 6 levels "Amazonia","Caatinga",..: 1 3 1 1 3 1 1 3 3 1 ...
$ diasemchuva : int 6 0 0 0 NA 2 7 NA 21 NA ...
$ precipitacao: num 0 0 0 0.9 NA 1.37 0 0 0.66 0 ...
$ riscofogo : num 1 1 0.75 0.68 NA 0.64 1 1 0.44 1 ...
$ latitude : num -10.01 -7.75 -9.53 -3.62 -13.03 ...
$ longitude : num -63.5 -46.8 -57.9 -54.3 -47.5 ...
$ frp : num 9 NA NA NA NA 17.8 15.5 NA 46 NA ...
Aparentemente, A coluna de área industrial e FRP tem poucas entradas não nulas
paste("Percentage of NAs entries on `FRP` =",
100*sum(is.na(df$frp))/totalNo)
[1] "Percentage of NAs entries on `FRP` = 68.8727954717493"
wetDays <- df$diasemchuva <= 10
dryDays <- df$diasemchuva > 10
noWeatherInfo <- is.na(df$diasemchuva)
paste("Percentage of NAs entries on `diasemchuva` =",
round(100*(sum(noWeatherInfo))/totalNo,2))
[1] "Percentage of NAs entries on `diasemchuva` = 45.43"
paste("Percentage of records with less than (or equal to) 10 days of no rain =",
round(100*(sum(wetDays, na.rm = T))/totalNo,2))
[1] "Percentage of records with less than (or equal to) 10 days of no rain = 42.72"
paste("Percentage of records with more than 10 days of no rain =",
round(100*(sum(dryDays, na.rm = T))/totalNo,2))
[1] "Percentage of records with more than 10 days of no rain = 11.85"
By the last results, it is possible that the number of days with no rain have a huge impact over the fires triggers.
we can also investigate another columns that have either redundant data or not useful data.
levels(df$pais)
[1] "Brasil"
levels(df$satelite)
[1] "AQUA_M-T"
df$pais <- NULL
df$satelite <- NULL
object_size(df)
7.61 MB
By looking at the structure of our dataframe, it becomes clear that the Datahora is a datetime object not a Factor.
df$datahora <- ymd_hms(str_replace_all(df$datahora,"/","-"))
levels(df$bioma)
[1] "Amazonia" "Caatinga" "Cerrado" "Mata Atlantica" "Pampa" "Pantanal"
We have 6 distincts biomas on our dataset. Perhaps, by knowing how is the distribution of fires among different biomas, we can refine our research.
counts <- table(df$bioma)
counts
Amazonia Caatinga Cerrado Mata Atlantica Pampa Pantanal
43954 5559 28766 7436 457 2339
proportions <- 100*counts / sum(counts)
proportions
Amazonia Caatinga Cerrado Mata Atlantica Pampa Pantanal
49.659364 6.280575 32.499915 8.401216 0.516320 2.642609
As we can see, Amazonia represents around half of our dataset. In addition, the number of fire occurrences is very much like the proportion of land that each bioma has in brazilian territory.
————————– Visualisations —————————–
ggplot(df) +
geom_bar(aes(x = bioma, y = 100*..prop.., group = 1)) +
coord_flip()

Now, it is also important to get the underlying trend of the number of fire ocurrences through the months of the year. Let’s start exploring this idea:
df %>%
mutate(month = floor_date(date(datahora), "month")) %>%
group_by(month, bioma) %>%
summarise(n_fires = n()) %>%
ggplot(aes(x = month, y = n_fires/1000)) +
geom_bar(aes(fill = bioma), stat = 'identity') +
scale_x_date(date_labels = "%b/%y", date_breaks = "6 months") +
labs(y = "Number of fires in thousands", title = 'Brazilian Wildfires by Year')

In order to plot the number of fires by state, we must do some data wrangling to put the data in the desired format.
is.odd <- function(x) x %% 2 != 0
is.even <- function(x) x %% 2 == 0
geo_data <- fromJSON(file = "./geo_data/geo_data.json")
maps_df <- list()
for (i in 1:length(geo_data$features)) {
coordinates <- unlist(geo_data$features[[i]]$geometry$coordinates[[1]])
long <- coordinates[is.odd(seq_along(coordinates))]
lat <- coordinates[is.even(seq_along(coordinates))]
if(length(long) != length(lat)){
stop("Wrong subset of coordinate vector", call. = FALSE)
}
geo_length <- length(long)
estado <- toupper(geo_data$features[[i]]$properties$name)
codigo <- as.integer(geo_data$features[[i]]$properties$codigo_ibg)
regiao <- as.integer(geo_data$features[[i]]$properties$regiao_id)
maps_df[[i]] <- as_tibble(
list(
estado = rep(stri_trans_general(estado,"Latin-ASCII"), geo_length),
codigo = rep(codigo, geo_length),
regiao = rep(regiao, geo_length),
longitude = long,
latitude = lat
)
)
}
map_df <- bind_rows(maps_df)
# the `estado` column of the map dataframe is a character, so in order
# to avoid coertion from factor to character, let's convert it explicitly
df$estado <- as.character(df$estado)
df %>%
select(estado) %>%
group_by(estado) %>%
summarise(n = n()) %>%
right_join(map_df, by = 'estado') %>%
ggplot(aes(x = longitude, y = latitude, group = codigo, fill = n/1000)) +
geom_polygon() +
geom_path(color = 'white', size = 0.1) +
scale_fill_continuous(low = "orange",
high = "darkred",
name = 'Number of fires\n(thousands)'
)

As we can see, the state with the most fire ocurrences is Pará in which most of its lands belongs to the amazon ecossystem, this may explain why half the dataset ocurrences comes from Amazon fires.
As we can see below, this trend happens all years.
df %>%
select(estado, datahora) %>%
mutate(ano = year(datahora)) %>%
group_by(estado, ano) %>%
summarise(n = n()) %>%
right_join(map_df, by = 'estado') %>%
ggplot(aes(x = longitude, y = latitude, group = codigo, fill = n/1000)) +
geom_polygon() +
facet_wrap(~ano) +
geom_path(color = 'white', size = 0.1) +
scale_fill_continuous(low = "orange",
high = "darkred",
name = 'Number of fires\n(thousands)'
)

In terms of raw numbers, we can visualize this difference below.
df %>%
group_by(estado) %>%
ggplot(aes(x = factor(estado))) +
geom_bar() +
coord_flip()

Going down one level, let’s try an analysis by county
geo_data_counties <- fromJSON(file = "./geo_data/geo_data_counties.json")
maps_df_counties <- list()
for (i in 1:length(geo_data_counties$features)) {
coordinates <- unlist(geo_data_counties$features[[i]]$geometry$coordinates[[1]])
long <- coordinates[is.odd(seq_along(coordinates))]
lat <- coordinates[is.even(seq_along(coordinates))]
if(length(long) != length(lat)){
stop("Wrong subset of coordinate vector", call. = FALSE)
}
geo_length <- length(long)
municipio <- geo_data_counties$features[[i]]$properties$name
municipio <- toupper(stri_trans_general(municipio, "Latin-ASCII"))
codigo <- as.integer(geo_data_counties$features[[i]]$properties$id)
maps_df_counties[[i]] <- as_tibble(
list(
municipio = rep(stri_trans_general(municipio, "Latin-ASCII"), geo_length),
codigo = rep(codigo, geo_length),
longitude = long,
latitude = lat
)
)
}
map_df_county <- bind_rows(maps_df_counties)
df %>%
select(municipio) %>%
group_by(municipio) %>%
summarise(n = n()) %>%
right_join(map_df_county, by = 'municipio') %>%
ggplot(aes(x = longitude, y = latitude, group = codigo, fill = n)) +
geom_polygon() +
geom_path(color = 'white', size = 0.1) +
scale_fill_continuous(low = "orange",
high = "darkred",
name = 'Number of fires'
)

What about that total number of fire ocurrences by state? From when does it come from?
df %>%
mutate(ano = year(datahora)) %>%
group_by(estado, ano) %>%
ggplot(aes(x = factor(estado), fill = factor(ano))) +
geom_bar() +
coord_flip()

Let’s repeat the last plot, but now considering the proportions
df %>%
mutate(ano = year(datahora)) %>%
group_by(estado, ano) %>%
ggplot(aes(x = factor(estado), fill = factor(ano))) +
geom_bar(position = "fill") +
coord_flip()

Let’s reinforce what we’ve seen by far and plot the data considering a monthly frequency
# Capitalizes names
capitalize <- function(x) {
s <- strsplit(x, " ")[[1]]
paste(toupper(substring(s, 1,1)), substring(s, 2),
sep="", collapse=" ")
}
levels <- c("Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro")
df %>%
mutate(mes = factor(sapply(months(datahora), capitalize), levels = levels)) %>%
group_by(mes) %>%
ggplot(aes(x = factor(mes), y = 100*..prop.., group = 1)) +
geom_bar()

df %>%
mutate(mes = factor(sapply(months(datahora), capitalize), levels = levels),
ano = factor(year(datahora))) %>%
group_by(ano, mes) %>%
ggplot(aes(x = mes, y = 100*..prop.., group = 1)) +
geom_bar() +
facet_wrap(~ano) + coord_flip()

As we can see, in 2019 the trend was a bit different, maybe it is so because of the popular protests against the liberal and progressive view of brazilian government towards amazonia.
Let’s compact this plot in only one frame:
levels <- c("Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez")
df %>%
mutate(mes = factor(sapply(substr(months(df$datahora), 1, 3), capitalize), levels = levels),
ano = factor(year(datahora))) %>%
group_by(ano, mes) %>%
ggplot(aes(x = mes, fill = ano)) +
geom_bar() +
facet_wrap(~ano) +
coord_flip()

df %>%
mutate(mes = factor(sapply(substr(months(df$datahora), 1, 3), capitalize), levels = levels),
ano = factor(year(datahora))) %>%
group_by(ano, mes) %>%
ggplot(aes(x = mes, fill = ano)) +
geom_bar(position = "dodge")

df %>%
mutate(date = date(datahora)) %>%
group_by(date) %>%
ggplot(aes(x = date, group = 1)) +
geom_freqpoly(stat = "count") +
scale_x_date(date_breaks = "6 months", date_labels = "%b/%y")

Strangely, our dataset only has records of fire occurences from 3 to 6 pm. Why is it?
df %>%
mutate(hour = hour(datahora)) %>%
group_by(hour) %>%
ggplot(aes(x = hour, group = 1)) +
geom_bar(stat = "count")

O dataframe original não tem informações sobre macro regiões. Utilizando o data frame de mapas, podemos obter esta informação.
regionsList <- list()
codMacroRegions <- list()
n_reg <- length(unique(map_df$regiao))
regions <- unique(map_df$regiao)
for(i in 1:n_reg){
temp <- map_df %>% filter(regiao == regions[i])
states <- unique(temp$estado)
regionsList[[i]] <- states
codMacroRegions[[i]] <- regions[i]
}
names(regionsList) <- c("NORTE", "NORDESTE", "SUDESTE", "CENTRO-OESTE", "SUL")
for(i in 1:n_reg){
df$macroRegiao[df$estado %in% regionsList[[i]]] <- names(regionsList)[i]
df$codMacroRegiao[df$estado %in% regionsList[[i]]] <- codMacroRegions[[i]]
}
# O nome regiao do dataframe map_df nao tem o mesmo sentido que no dataframe df
# sendo assim, criou-se um dataframe auxiliar com os nomes correspondentes
temp <- df[,c("macroRegiao", "codMacroRegiao")]
names(temp) <- c("macroregiao", "regiao")
temp %>%
group_by(macroregiao, regiao) %>%
summarise(n = n()) %>%
right_join(map_df, by = 'regiao') %>%
ggplot(aes(x = longitude, y = latitude, group = regiao, fill = n/1000)) +
geom_polygon() +
geom_path(color = 'white', size = 0.1) +
scale_fill_continuous(low = "orange",
high = "darkred",
name = 'Number of fires\n(thousands)'
)

na.omit(df) %>% ggplot(aes(x = frp, y = precipitacao, alpha = riscofogo)) +
geom_point()

na.omit(df) %>% ggplot(aes(x = log(frp), y = precipitacao, alpha = riscofogo)) +
geom_point()

Notice the relationship among the fire radiative power, the precipitation and the risk of fire. It seems that, with more precipitation, the frp is reduced while with less precipitation the risk of fire remains mostly
na.omit(df) %>% ggplot(aes(x = riscofogo, y = precipitacao, alpha = frp)) +
geom_point(position = "jitter")

Looking at the distribution of the fire radiative power across different biomas, we can see that it is roughly the same, except for Mata Atlantica and Pampa maybe because Pampa is situated at the south brazil, where temperatures are lower which decreases the chances of fire ocurrences. On the other side, Mata Atlantica is mostly distributed along the coast, implying that the humidity that comes from the ocean may hold fire ocurrences from stroke.
na.omit(df) %>% ggplot(aes(x = bioma, y = log(frp))) +
geom_boxplot()

p <- na.omit(df) %>% ggplot(aes(x = log(frp), col = bioma, fill = bioma)) +
geom_density(alpha = 0.1)
ggplotly(p)
Removed 1 rows containing non-finite values (stat_density).
As we can see below, we cannot take any conclusion about the risk of fire with respect to the fire radiative power. So sad.
na.omit(df) %>%
ggplot(aes(x = riscofogo, y = log(frp), col = bioma)) +
geom_point() + # Copy from Plot 1
geom_smooth(method = "lm", se = FALSE) +
geom_smooth(aes(group = 1), method = "lm", se = FALSE, linetype = 2)

data19 <- df %>% filter(year(datahora) == '2019')
df %>%
select(estado) %>%
group_by(estado) %>%
summarise(n = n()) %>%
right_join(map_df, by = 'estado') %>%
ggplot(aes(x = longitude, y = latitude, group = codigo)) +
geom_polygon() +
geom_point(inherit.aes = F, data = data19, aes(x = longitude, y = latitude, col = bioma), size = 0.1, shape = 3) +
geom_path(color = 'white', size = 0.1)

data_yearly <- df %>% mutate(ano = year(datahora))
df %>%
select(estado, datahora) %>%
mutate(ano = year(datahora)) %>%
group_by(estado, ano) %>%
summarise(n = n()) %>%
right_join(map_df, by = 'estado') %>%
ggplot(aes(x = longitude, y = latitude, group = codigo)) +
geom_polygon() +
geom_point(inherit.aes = F, data = data_yearly, aes(x = longitude, y = latitude, col = bioma), size = 0.05) +
facet_wrap(~ano) +
geom_path(color = 'white', size = 0.1)

#
#
From NasaPower API we can obtain useful data for about 146 weather and climate parameters, such as:
Relative humidty (at two meters) Temperature (at two meters) Precipitation (mm) Maximum/Minimum Monthly Difference From Monthly Averaged All Sky Insolation Wind Speed
geographical data are provided at the resolution of 1/2 arc degree longitude by 1/2 arc degree latitude. for more detailed info go to https://power.larc.nasa.gov/documents/POWER_Data_v9_methodology.pdf
Another useful resource can be found at https://earthobservatory.nasa.gov/images/145464/fires-in-brazil
Also in https://www.globalfiredata.org/data.html we can find data such as carbon emissions and dry matter emissions.
# nasaViirs <- read.csv("./database/DL_FIRE_V1_77994/fire_archive_V1_77994.csv")
# nasaModis <- read.csv("./database/DL_FIRE_M6_78067/fire_archive_M6_78067.csv")
# object_size(nasaModis)
# object_size(nasaViirs)
# View(nasaModis)
# View(nasaViirs)
LS0tCnRpdGxlOiAiV2lsZEZpcmVzIEFuYWx5c2lzIC0gRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBMb2FkaW5nIHBhY2thZ2VzCmxpYnJhcnkocHJ5cikgIyBNZW1vcnkgdXNhZ2UgaW5mb3JtYXRpb24KbGlicmFyeShsdWJyaWRhdGUpICMgZGF0ZSBtYW5pcHVsYXRpb24KbGlicmFyeShkcGx5cikgIyBkYXRhIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KHRpZHlyKSAjIGRhdGEgbWFuaXB1bGF0aW9uCmxpYnJhcnkoZ2dwbG90MikgIyBncmFwaGljcwpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHN0cmluZ2kpICMgcmVtb3ZlIGRpYWNyaXRpY3MKbGlicmFyeShnZ2ZvcnRpZnkpCmxpYnJhcnkocmpzb24pCmxpYnJhcnkobmFzYXBvd2VyKQpgYGAKCgpUaGVyZSBhcmUgNCBjc3YgZmlsZXMgY29udGFpbmluZyBkYXRhIGFib3V0IHRoZSBmaXJlcyBvZiBhbGwgb2N1cnJlbmNlcyBhcm91bmQgYWxsIGJyYXppbGlhbiB0ZXJyaXRvcnkuIEVhY2ggZmlsZSBoYXMgZGF0YSBiZXR3ZWVuIDFzdCBTZXB0IHRvIDMwdGggU2VwdCBvZiBlYWNoIHllYXIgZnJvbSAyMDE1IHRvIDIwMTkuCgpgYGB7cn0KdGVtcCA8LSBsaXN0LmZpbGVzKHBhdGggPSAiLi9kYXRhYmFzZSIgLHBhdHRlcm4gPSAiKi5jc3YiKQpteWZpbGVzID0gbGFwcGx5KHBhc3RlMCgiLi9kYXRhYmFzZS8iLHRlbXApLCByZWFkLmNzdikKCm5yZWNvcmRzIDwtIGxhcHBseShteWZpbGVzLCBucm93KQoKZGF0YSA8LSBiaW5kX3Jvd3MobXlmaWxlcykKbmFtZXMoZGF0YSkKCiMgQXMgd2UgaGF2ZSBtYW55IGRhdGEgcmVjb3Jkcywgd2UnbGwgYmUgd29ya2luZyB3aXRoIGEgc21hbGwgc3Vic2V0IG9mIGl0CmRmIDwtIGRhdGFbc2FtcGxlKG5yb3coZGF0YSksIDAuMSpucm93KGRhdGEpKSwgXQp0b3RhbE5vIDwtIG5yb3coZGYpCgpvYmplY3Rfc2l6ZShkZikKYGBgCgp0aGVyZSBhcmUgODIwOTI5IG9ic2VydmF0aW9ucyBpbiBvdXIgZGF0YXNldCB3aGljaCBzdW1zIHVwIHRvIDY2LjggTUIgb2Ygc3RvcmFnZWQgZGF0YS4KCmBgYHtyfQpoZWFkKGRmKQpgYGAKCmBgYHtyfQpzdHIoZGYpCmBgYAoKQXBhcmVudGVtZW50ZSwgQSBjb2x1bmEgZGUgw6FyZWEgaW5kdXN0cmlhbCBlIEZSUCB0ZW0gcG91Y2FzIGVudHJhZGFzIG7Do28gbnVsYXMKCgpgYGB7cn0KcGFzdGUoIlBlcmNlbnRhZ2Ugb2YgTkFzIGVudHJpZXMgb24gYEZSUGAgPSIsCiAgICAgIDEwMCpzdW0oaXMubmEoZGYkZnJwKSkvdG90YWxObykKCndldERheXMgPC0gZGYkZGlhc2VtY2h1dmEgPD0gMTAKZHJ5RGF5cyA8LSBkZiRkaWFzZW1jaHV2YSA+IDEwCm5vV2VhdGhlckluZm8gPC0gaXMubmEoZGYkZGlhc2VtY2h1dmEpCgpwYXN0ZSgiUGVyY2VudGFnZSBvZiBOQXMgZW50cmllcyBvbiBgZGlhc2VtY2h1dmFgID0iLAogICAgICByb3VuZCgxMDAqKHN1bShub1dlYXRoZXJJbmZvKSkvdG90YWxObywyKSkKCnBhc3RlKCJQZXJjZW50YWdlIG9mIHJlY29yZHMgd2l0aCBsZXNzIHRoYW4gKG9yIGVxdWFsIHRvKSAxMCBkYXlzIG9mIG5vIHJhaW4gPSIsCiAgICAgIHJvdW5kKDEwMCooc3VtKHdldERheXMsIG5hLnJtID0gVCkpL3RvdGFsTm8sMikpCgpwYXN0ZSgiUGVyY2VudGFnZSBvZiByZWNvcmRzIHdpdGggbW9yZSB0aGFuIDEwIGRheXMgb2Ygbm8gcmFpbiA9IiwKICAgICAgcm91bmQoMTAwKihzdW0oZHJ5RGF5cywgbmEucm0gPSBUKSkvdG90YWxObywyKSkKYGBgCgpCeSB0aGUgbGFzdCByZXN1bHRzLCBpdCBpcyBwb3NzaWJsZSB0aGF0IHRoZSBudW1iZXIgb2YgZGF5cyB3aXRoIG5vIHJhaW4gaGF2ZSBhIGh1Z2UgaW1wYWN0IG92ZXIgdGhlIGZpcmVzIHRyaWdnZXJzLgoKd2UgY2FuIGFsc28gaW52ZXN0aWdhdGUgYW5vdGhlciBjb2x1bW5zIHRoYXQgaGF2ZSBlaXRoZXIgcmVkdW5kYW50IGRhdGEgb3Igbm90IHVzZWZ1bCBkYXRhLgoKYGBge3J9CmxldmVscyhkZiRwYWlzKQpsZXZlbHMoZGYkc2F0ZWxpdGUpCgpkZiRwYWlzIDwtIE5VTEwKZGYkc2F0ZWxpdGUgPC0gTlVMTAoKb2JqZWN0X3NpemUoZGYpCmBgYAoKQnkgbG9va2luZyBhdCB0aGUgc3RydWN0dXJlIG9mIG91ciBkYXRhZnJhbWUsIGl0IGJlY29tZXMgY2xlYXIgdGhhdCB0aGUgYERhdGFob3JhYCBpcyBhIGRhdGV0aW1lIG9iamVjdCBub3QgYSBGYWN0b3IuCgpgYGB7cn0KZGYkZGF0YWhvcmEgPC0geW1kX2htcyhzdHJfcmVwbGFjZV9hbGwoZGYkZGF0YWhvcmEsIi8iLCItIikpCgpsZXZlbHMoZGYkYmlvbWEpCmBgYAoKV2UgaGF2ZSA2IGRpc3RpbmN0cyBiaW9tYXMgb24gb3VyIGRhdGFzZXQuIFBlcmhhcHMsIGJ5IGtub3dpbmcgaG93IGlzIHRoZSBkaXN0cmlidXRpb24gb2YgZmlyZXMgYW1vbmcgZGlmZmVyZW50IGJpb21hcywgd2UgY2FuIHJlZmluZSBvdXIgcmVzZWFyY2guCgpgYGB7cn0KY291bnRzIDwtIHRhYmxlKGRmJGJpb21hKQpjb3VudHMKCnByb3BvcnRpb25zIDwtIDEwMCpjb3VudHMgLyBzdW0oY291bnRzKQpwcm9wb3J0aW9ucwpgYGAKCkFzIHdlIGNhbiBzZWUsIEFtYXpvbmlhIHJlcHJlc2VudHMgYXJvdW5kIGhhbGYgb2Ygb3VyIGRhdGFzZXQuIEluIGFkZGl0aW9uLCB0aGUgbnVtYmVyIG9mIGZpcmUgb2NjdXJyZW5jZXMgaXMgdmVyeSBtdWNoIGxpa2UgdGhlIHByb3BvcnRpb24gb2YgbGFuZCB0aGF0IGVhY2ggYmlvbWEgaGFzIGluIGJyYXppbGlhbiB0ZXJyaXRvcnkuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBWaXN1YWxpc2F0aW9ucyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKYGBge3IsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQ9IDN9CmdncGxvdChkZikgKyAKICAgIGdlb21fYmFyKGFlcyh4ID0gYmlvbWEsIHkgPSAxMDAqLi5wcm9wLi4sIGdyb3VwID0gMSkpICsKICAgIGNvb3JkX2ZsaXAoKQpgYGAKCk5vdywgaXQgaXMgYWxzbyBpbXBvcnRhbnQgdG8gZ2V0IHRoZSB1bmRlcmx5aW5nIHRyZW5kIG9mIHRoZSBudW1iZXIgb2YgZmlyZSBvY3VycmVuY2VzIHRocm91Z2ggdGhlIG1vbnRocyBvZiB0aGUgeWVhci4gTGV0J3Mgc3RhcnQgZXhwbG9yaW5nIHRoaXMgaWRlYToKCmBgYHtyfQpkZiAlPiUgCiAgbXV0YXRlKG1vbnRoID0gZmxvb3JfZGF0ZShkYXRlKGRhdGFob3JhKSwgIm1vbnRoIikpICU+JSAKICBncm91cF9ieShtb250aCwgYmlvbWEpICU+JSAKICBzdW1tYXJpc2Uobl9maXJlcyA9IG4oKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IG1vbnRoLCB5ID0gbl9maXJlcy8xMDAwKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsID0gYmlvbWEpLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICAgIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscyA9ICIlYi8leSIsIGRhdGVfYnJlYWtzID0gIjYgbW9udGhzIikgKwogICAgbGFicyh5ID0gIk51bWJlciBvZiBmaXJlcyBpbiB0aG91c2FuZHMiLCB0aXRsZSA9ICdCcmF6aWxpYW4gV2lsZGZpcmVzIGJ5IFllYXInKQpgYGAKCkluIG9yZGVyIHRvIHBsb3QgdGhlIG51bWJlciBvZiBmaXJlcyBieSBzdGF0ZSwgd2UgbXVzdCBkbyBzb21lIGRhdGEgd3JhbmdsaW5nIHRvIHB1dCB0aGUgZGF0YSBpbiB0aGUgZGVzaXJlZCBmb3JtYXQuCgpgYGB7cn0KCmlzLm9kZCA8LSBmdW5jdGlvbih4KSB4ICUlIDIgIT0gMAppcy5ldmVuIDwtIGZ1bmN0aW9uKHgpIHggJSUgMiA9PSAwCgpnZW9fZGF0YSA8LSBmcm9tSlNPTihmaWxlID0gIi4vZ2VvX2RhdGEvZ2VvX2RhdGEuanNvbiIpCgptYXBzX2RmIDwtIGxpc3QoKQogIGZvciAoaSBpbiAxOmxlbmd0aChnZW9fZGF0YSRmZWF0dXJlcykpIHsKICAgIGNvb3JkaW5hdGVzIDwtIHVubGlzdChnZW9fZGF0YSRmZWF0dXJlc1tbaV1dJGdlb21ldHJ5JGNvb3JkaW5hdGVzW1sxXV0pCiAgICBsb25nIDwtIGNvb3JkaW5hdGVzW2lzLm9kZChzZXFfYWxvbmcoY29vcmRpbmF0ZXMpKV0KICAgIGxhdCA8LSAgY29vcmRpbmF0ZXNbaXMuZXZlbihzZXFfYWxvbmcoY29vcmRpbmF0ZXMpKV0KICAgIAogICAgaWYobGVuZ3RoKGxvbmcpICE9IGxlbmd0aChsYXQpKXsKICAgICAgc3RvcCgiV3Jvbmcgc3Vic2V0IG9mIGNvb3JkaW5hdGUgdmVjdG9yIiwgY2FsbC4gPSBGQUxTRSkKICAgIH0KICAgIAogICAgZ2VvX2xlbmd0aCA8LSBsZW5ndGgobG9uZykKICAgIAogICAgZXN0YWRvIDwtIHRvdXBwZXIoZ2VvX2RhdGEkZmVhdHVyZXNbW2ldXSRwcm9wZXJ0aWVzJG5hbWUpCiAgICBjb2RpZ28gPC0gYXMuaW50ZWdlcihnZW9fZGF0YSRmZWF0dXJlc1tbaV1dJHByb3BlcnRpZXMkY29kaWdvX2liZykKICAgIHJlZ2lhbyA8LSBhcy5pbnRlZ2VyKGdlb19kYXRhJGZlYXR1cmVzW1tpXV0kcHJvcGVydGllcyRyZWdpYW9faWQpCiAgICAKICAgIG1hcHNfZGZbW2ldXSA8LSBhc190aWJibGUoCiAgICAgIGxpc3QoCiAgICAgICAgZXN0YWRvID0gcmVwKHN0cmlfdHJhbnNfZ2VuZXJhbChlc3RhZG8sIkxhdGluLUFTQ0lJIiksIGdlb19sZW5ndGgpLAogICAgICAgIGNvZGlnbyA9IHJlcChjb2RpZ28sIGdlb19sZW5ndGgpLAogICAgICAgIHJlZ2lhbyA9IHJlcChyZWdpYW8sIGdlb19sZW5ndGgpLAogICAgICAgIGxvbmdpdHVkZSA9IGxvbmcsCiAgICAgICAgbGF0aXR1ZGUgPSBsYXQKICAgICAgKQogICAgKQogIH0KICAKICBtYXBfZGYgPC0gYmluZF9yb3dzKG1hcHNfZGYpCmBgYAoKYGBge3J9CiMgdGhlIGBlc3RhZG9gIGNvbHVtbiBvZiB0aGUgbWFwIGRhdGFmcmFtZSBpcyBhIGNoYXJhY3Rlciwgc28gaW4gb3JkZXIKIyB0byBhdm9pZCBjb2VydGlvbiBmcm9tIGZhY3RvciB0byBjaGFyYWN0ZXIsIGxldCdzIGNvbnZlcnQgaXQgZXhwbGljaXRseQpkZiRlc3RhZG8gPC0gYXMuY2hhcmFjdGVyKGRmJGVzdGFkbykKYGBgCgpgYGB7cn0KZGYgJT4lIAogIHNlbGVjdChlc3RhZG8pICU+JSAKICBncm91cF9ieShlc3RhZG8pICU+JSAKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lIAogIHJpZ2h0X2pvaW4obWFwX2RmLCBieSA9ICdlc3RhZG8nKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGdyb3VwID0gY29kaWdvLCBmaWxsID0gbi8xMDAwKSkgKyAKICAgIGdlb21fcG9seWdvbigpICsgCiAgICBnZW9tX3BhdGgoY29sb3IgPSAnd2hpdGUnLCBzaXplID0gMC4xKSArIAogICAgc2NhbGVfZmlsbF9jb250aW51b3VzKGxvdyA9ICJvcmFuZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gImRhcmtyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnTnVtYmVyIG9mIGZpcmVzXG4odGhvdXNhbmRzKScKICAgICAgICAgICAgICAgICAgICAgICAgICApIApgYGAKCkFzIHdlIGNhbiBzZWUsIHRoZSBzdGF0ZSB3aXRoIHRoZSBtb3N0IGZpcmUgb2N1cnJlbmNlcyBpcyBQYXLDoSBpbiB3aGljaCBtb3N0IG9mIGl0cyBsYW5kcyBiZWxvbmdzIHRvIHRoZSBhbWF6b24gZWNvc3N5c3RlbSwgdGhpcyBtYXkgZXhwbGFpbiB3aHkgaGFsZiB0aGUgZGF0YXNldCBvY3VycmVuY2VzIGNvbWVzIGZyb20gQW1hem9uIGZpcmVzLgoKQXMgd2UgY2FuIHNlZSBiZWxvdywgdGhpcyB0cmVuZCBoYXBwZW5zIGFsbCB5ZWFycy4KCmBgYHtyfQpkZiAlPiUgCiAgc2VsZWN0KGVzdGFkbywgZGF0YWhvcmEpICU+JSAKICBtdXRhdGUoYW5vID0geWVhcihkYXRhaG9yYSkpICU+JSAKICBncm91cF9ieShlc3RhZG8sIGFubykgJT4lIAogIHN1bW1hcmlzZShuID0gbigpKSAlPiUgCiAgcmlnaHRfam9pbihtYXBfZGYsIGJ5ID0gJ2VzdGFkbycpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSwgZ3JvdXAgPSBjb2RpZ28sIGZpbGwgPSBuLzEwMDApKSArIAogICAgZ2VvbV9wb2x5Z29uKCkgKwogICAgZmFjZXRfd3JhcCh+YW5vKSArCiAgICBnZW9tX3BhdGgoY29sb3IgPSAnd2hpdGUnLCBzaXplID0gMC4xKSArIAogICAgc2NhbGVfZmlsbF9jb250aW51b3VzKGxvdyA9ICJvcmFuZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gImRhcmtyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnTnVtYmVyIG9mIGZpcmVzXG4odGhvdXNhbmRzKScKICAgICAgICAgICAgICAgICAgICAgICAgICApIApgYGAKCkluIHRlcm1zIG9mIHJhdyBudW1iZXJzLCB3ZSBjYW4gdmlzdWFsaXplIHRoaXMgZGlmZmVyZW5jZSBiZWxvdy4KCmBgYHtyfQpkZiAlPiUgCiAgZ3JvdXBfYnkoZXN0YWRvKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKGVzdGFkbykpKSArCiAgICBnZW9tX2JhcigpICsKICAgIGNvb3JkX2ZsaXAoKQpgYGAKCkdvaW5nIGRvd24gb25lIGxldmVsLCBsZXQncyB0cnkgYW4gYW5hbHlzaXMgYnkgY291bnR5CgpgYGB7cn0KZ2VvX2RhdGFfY291bnRpZXMgPC0gZnJvbUpTT04oZmlsZSA9ICIuL2dlb19kYXRhL2dlb19kYXRhX2NvdW50aWVzLmpzb24iKQoKbWFwc19kZl9jb3VudGllcyA8LSBsaXN0KCkKICBmb3IgKGkgaW4gMTpsZW5ndGgoZ2VvX2RhdGFfY291bnRpZXMkZmVhdHVyZXMpKSB7CiAgICBjb29yZGluYXRlcyA8LSB1bmxpc3QoZ2VvX2RhdGFfY291bnRpZXMkZmVhdHVyZXNbW2ldXSRnZW9tZXRyeSRjb29yZGluYXRlc1tbMV1dKQogICAgbG9uZyA8LSBjb29yZGluYXRlc1tpcy5vZGQoc2VxX2Fsb25nKGNvb3JkaW5hdGVzKSldCiAgICBsYXQgPC0gIGNvb3JkaW5hdGVzW2lzLmV2ZW4oc2VxX2Fsb25nKGNvb3JkaW5hdGVzKSldCiAgICAKICAgIGlmKGxlbmd0aChsb25nKSAhPSBsZW5ndGgobGF0KSl7CiAgICAgIHN0b3AoIldyb25nIHN1YnNldCBvZiBjb29yZGluYXRlIHZlY3RvciIsIGNhbGwuID0gRkFMU0UpCiAgICB9CiAgICAKICAgIGdlb19sZW5ndGggPC0gbGVuZ3RoKGxvbmcpCiAgICAKICAgIG11bmljaXBpbyA8LSBnZW9fZGF0YV9jb3VudGllcyRmZWF0dXJlc1tbaV1dJHByb3BlcnRpZXMkbmFtZQogICAgbXVuaWNpcGlvIDwtIHRvdXBwZXIoc3RyaV90cmFuc19nZW5lcmFsKG11bmljaXBpbywgIkxhdGluLUFTQ0lJIikpCiAgICBjb2RpZ28gPC0gYXMuaW50ZWdlcihnZW9fZGF0YV9jb3VudGllcyRmZWF0dXJlc1tbaV1dJHByb3BlcnRpZXMkaWQpCiAgICAKICAgIG1hcHNfZGZfY291bnRpZXNbW2ldXSA8LSBhc190aWJibGUoCiAgICAgIGxpc3QoCiAgICAgICAgbXVuaWNpcGlvID0gcmVwKHN0cmlfdHJhbnNfZ2VuZXJhbChtdW5pY2lwaW8sICJMYXRpbi1BU0NJSSIpLCBnZW9fbGVuZ3RoKSwKICAgICAgICBjb2RpZ28gPSByZXAoY29kaWdvLCBnZW9fbGVuZ3RoKSwKICAgICAgICBsb25naXR1ZGUgPSBsb25nLAogICAgICAgIGxhdGl0dWRlID0gbGF0CiAgICAgICkKICAgICkKICB9CiAgCiAgbWFwX2RmX2NvdW50eSA8LSBiaW5kX3Jvd3MobWFwc19kZl9jb3VudGllcykKYGBgCgpgYGB7cn0KZGYgJT4lIAogIHNlbGVjdChtdW5pY2lwaW8pICU+JSAKICBncm91cF9ieShtdW5pY2lwaW8pICU+JSAKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lIAogIHJpZ2h0X2pvaW4obWFwX2RmX2NvdW50eSwgYnkgPSAnbXVuaWNpcGlvJykgJT4lIAogIGdncGxvdChhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBncm91cCA9IGNvZGlnbywgZmlsbCA9IG4pKSArIAogICAgZ2VvbV9wb2x5Z29uKCkgKyAKICAgIGdlb21fcGF0aChjb2xvciA9ICd3aGl0ZScsIHNpemUgPSAwLjEpICsKICAgIHNjYWxlX2ZpbGxfY29udGludW91cyhsb3cgPSAib3JhbmdlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICJkYXJrcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ051bWJlciBvZiBmaXJlcycKICAgICAgICAgICAgICAgICAgICAgICAgICApIApgYGAKCldoYXQgYWJvdXQgdGhhdCB0b3RhbCBudW1iZXIgb2YgZmlyZSBvY3VycmVuY2VzIGJ5IHN0YXRlPyBGcm9tIHdoZW4gZG9lcyBpdCBjb21lIGZyb20/CgpgYGB7cn0KZGYgJT4lIAogIG11dGF0ZShhbm8gPSB5ZWFyKGRhdGFob3JhKSkgJT4lIAogIGdyb3VwX2J5KGVzdGFkbywgYW5vKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKGVzdGFkbyksIGZpbGwgPSBmYWN0b3IoYW5vKSkpICsKICAgIGdlb21fYmFyKCkgKwogICAgY29vcmRfZmxpcCgpCmBgYAoKTGV0J3MgcmVwZWF0IHRoZSBsYXN0IHBsb3QsIGJ1dCBub3cgY29uc2lkZXJpbmcgdGhlIHByb3BvcnRpb25zCgpgYGB7cn0KZGYgJT4lIAogIG11dGF0ZShhbm8gPSB5ZWFyKGRhdGFob3JhKSkgJT4lIAogIGdyb3VwX2J5KGVzdGFkbywgYW5vKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKGVzdGFkbyksIGZpbGwgPSBmYWN0b3IoYW5vKSkpICsKICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArCiAgICBjb29yZF9mbGlwKCkKYGBgCgpMZXQncyByZWluZm9yY2Ugd2hhdCB3ZSd2ZSBzZWVuIGJ5IGZhciBhbmQgcGxvdCB0aGUgZGF0YSBjb25zaWRlcmluZyBhIG1vbnRobHkgZnJlcXVlbmN5CgpgYGB7cn0KIyBDYXBpdGFsaXplcyBuYW1lcwoKY2FwaXRhbGl6ZSA8LSBmdW5jdGlvbih4KSB7CiAgcyA8LSBzdHJzcGxpdCh4LCAiICIpW1sxXV0KICBwYXN0ZSh0b3VwcGVyKHN1YnN0cmluZyhzLCAxLDEpKSwgc3Vic3RyaW5nKHMsIDIpLAogICAgICBzZXA9IiIsIGNvbGxhcHNlPSIgIikKfQpgYGAKCgpgYGB7cn0KbGV2ZWxzIDwtIGMoIkphbmVpcm8iLCAiRmV2ZXJlaXJvIiwgIk1hcsOnbyIsICJBYnJpbCIsICJNYWlvIiwgIkp1bmhvIiwgIkp1bGhvIiwgIkFnb3N0byIsICJTZXRlbWJybyIsICJPdXR1YnJvIiwgIk5vdmVtYnJvIiwgIkRlemVtYnJvIikKCmRmICU+JSAKICBtdXRhdGUobWVzID0gZmFjdG9yKHNhcHBseShtb250aHMoZGF0YWhvcmEpLCBjYXBpdGFsaXplKSwgbGV2ZWxzID0gbGV2ZWxzKSkgJT4lCiAgZ3JvdXBfYnkobWVzKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IobWVzKSwgeSA9IDEwMCouLnByb3AuLiwgZ3JvdXAgPSAxKSkgKwogICAgZ2VvbV9iYXIoKQoKZGYgJT4lIAogIG11dGF0ZShtZXMgPSBmYWN0b3Ioc2FwcGx5KG1vbnRocyhkYXRhaG9yYSksIGNhcGl0YWxpemUpLCBsZXZlbHMgPSBsZXZlbHMpLAogICAgICAgICBhbm8gPSBmYWN0b3IoeWVhcihkYXRhaG9yYSkpKSAlPiUKICBncm91cF9ieShhbm8sIG1lcykgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbWVzLCB5ID0gMTAwKi4ucHJvcC4uLCBncm91cCA9IDEpKSArCiAgICBnZW9tX2JhcigpICsgCiAgICBmYWNldF93cmFwKH5hbm8pICsgY29vcmRfZmxpcCgpCmBgYAoKQXMgd2UgY2FuIHNlZSwgaW4gMjAxOSB0aGUgdHJlbmQgd2FzIGEgYml0IGRpZmZlcmVudCwgbWF5YmUgaXQgaXMgc28gYmVjYXVzZSBvZiB0aGUgcG9wdWxhciBwcm90ZXN0cyBhZ2FpbnN0IHRoZSBsaWJlcmFsIGFuZCBwcm9ncmVzc2l2ZSB2aWV3IG9mIGJyYXppbGlhbiBnb3Zlcm5tZW50IHRvd2FyZHMgYW1hem9uaWEuCgpMZXQncyBjb21wYWN0IHRoaXMgcGxvdCBpbiBvbmx5IG9uZSBmcmFtZToKCmBgYHtyfQoKbGV2ZWxzIDwtIGMoIkphbiIsICJGZXYiLCAiTWFyIiwgIkFiciIsICJNYWkiLCAiSnVuIiwgIkp1bCIsICJBZ28iLCAiU2V0IiwgIk91dCIsICJOb3YiLCAiRGV6IikKCmRmICU+JSAKICBtdXRhdGUobWVzID0gZmFjdG9yKHNhcHBseShzdWJzdHIobW9udGhzKGRmJGRhdGFob3JhKSwgMSwgMyksIGNhcGl0YWxpemUpLCBsZXZlbHMgPSBsZXZlbHMpLAogICAgICAgICBhbm8gPSBmYWN0b3IoeWVhcihkYXRhaG9yYSkpKSAlPiUKICBncm91cF9ieShhbm8sIG1lcykgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbWVzLCBmaWxsID0gYW5vKSkgKwogICAgZ2VvbV9iYXIoKSArIAogICAgZmFjZXRfd3JhcCh+YW5vKSArCiAgICBjb29yZF9mbGlwKCkKCmRmICU+JSAKICBtdXRhdGUobWVzID0gZmFjdG9yKHNhcHBseShzdWJzdHIobW9udGhzKGRmJGRhdGFob3JhKSwgMSwgMyksIGNhcGl0YWxpemUpLCBsZXZlbHMgPSBsZXZlbHMpLAogICAgICAgICBhbm8gPSBmYWN0b3IoeWVhcihkYXRhaG9yYSkpKSAlPiUKICBncm91cF9ieShhbm8sIG1lcykgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbWVzLCBmaWxsID0gYW5vKSkgKwogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQoKYGBgCgpgYGB7cn0KZGYgJT4lIAogIG11dGF0ZShkYXRlID0gZGF0ZShkYXRhaG9yYSkpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgZ3JvdXAgPSAxKSkgKwogICBnZW9tX2ZyZXFwb2x5KHN0YXQgPSAiY291bnQiKSArCiAgIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICI2IG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiViLyV5IikKYGBgCgpTdHJhbmdlbHksIG91ciBkYXRhc2V0IG9ubHkgaGFzIHJlY29yZHMgb2YgZmlyZSBvY2N1cmVuY2VzIGZyb20gMyB0byA2IHBtLiBXaHkgaXMgaXQ/CgpgYGB7cn0KZGYgJT4lIAogIG11dGF0ZShob3VyID0gaG91cihkYXRhaG9yYSkpICU+JSAKICBncm91cF9ieShob3VyKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gaG91ciwgZ3JvdXAgPSAxKSkgKwogICBnZW9tX2JhcihzdGF0ID0gImNvdW50IikKYGBgCgpPIGRhdGFmcmFtZSBvcmlnaW5hbCBuw6NvIHRlbSBpbmZvcm1hw6fDtWVzIHNvYnJlIG1hY3JvIHJlZ2nDtWVzLiBVdGlsaXphbmRvIG8gZGF0YSBmcmFtZSBkZSBtYXBhcywgcG9kZW1vcyBvYnRlciBlc3RhIGluZm9ybWHDp8Ojby4KCmBgYHtyfQoKcmVnaW9uc0xpc3QgPC0gbGlzdCgpCmNvZE1hY3JvUmVnaW9ucyA8LSBsaXN0KCkKbl9yZWcgPC0gbGVuZ3RoKHVuaXF1ZShtYXBfZGYkcmVnaWFvKSkKcmVnaW9ucyA8LSB1bmlxdWUobWFwX2RmJHJlZ2lhbykKCmZvcihpIGluIDE6bl9yZWcpewogIHRlbXAgPC0gbWFwX2RmICU+JSBmaWx0ZXIocmVnaWFvID09IHJlZ2lvbnNbaV0pCiAgCiAgc3RhdGVzIDwtIHVuaXF1ZSh0ZW1wJGVzdGFkbykKICByZWdpb25zTGlzdFtbaV1dIDwtIHN0YXRlcwogIGNvZE1hY3JvUmVnaW9uc1tbaV1dIDwtIHJlZ2lvbnNbaV0KfQoKbmFtZXMocmVnaW9uc0xpc3QpIDwtIGMoIk5PUlRFIiwgIk5PUkRFU1RFIiwgIlNVREVTVEUiLCAiQ0VOVFJPLU9FU1RFIiwgIlNVTCIpCgpmb3IoaSBpbiAxOm5fcmVnKXsKICBkZiRtYWNyb1JlZ2lhb1tkZiRlc3RhZG8gJWluJSByZWdpb25zTGlzdFtbaV1dXSA8LSBuYW1lcyhyZWdpb25zTGlzdClbaV0KICBkZiRjb2RNYWNyb1JlZ2lhb1tkZiRlc3RhZG8gJWluJSByZWdpb25zTGlzdFtbaV1dXSA8LSBjb2RNYWNyb1JlZ2lvbnNbW2ldXQp9CmBgYAoKYGBge3J9CgojIE8gbm9tZSByZWdpYW8gZG8gZGF0YWZyYW1lIG1hcF9kZiBuYW8gdGVtIG8gbWVzbW8gc2VudGlkbyBxdWUgbm8gZGF0YWZyYW1lIGRmCiMgc2VuZG8gYXNzaW0sIGNyaW91LXNlIHVtIGRhdGFmcmFtZSBhdXhpbGlhciBjb20gb3Mgbm9tZXMgY29ycmVzcG9uZGVudGVzCnRlbXAgPC0gZGZbLGMoIm1hY3JvUmVnaWFvIiwgImNvZE1hY3JvUmVnaWFvIildCm5hbWVzKHRlbXApIDwtIGMoIm1hY3JvcmVnaWFvIiwgInJlZ2lhbyIpCgp0ZW1wICU+JSAKICBncm91cF9ieShtYWNyb3JlZ2lhbywgcmVnaWFvKSAlPiUgCiAgc3VtbWFyaXNlKG4gPSBuKCkpICU+JSAKICByaWdodF9qb2luKG1hcF9kZiwgYnkgPSAncmVnaWFvJykgJT4lIAogIGdncGxvdChhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBncm91cCA9IHJlZ2lhbywgZmlsbCA9IG4vMTAwMCkpICsgCiAgICBnZW9tX3BvbHlnb24oKSArIAogICAgZ2VvbV9wYXRoKGNvbG9yID0gJ3doaXRlJywgc2l6ZSA9IDAuMSkgKyAKICAgIHNjYWxlX2ZpbGxfY29udGludW91cyhsb3cgPSAib3JhbmdlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICJkYXJrcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ051bWJlciBvZiBmaXJlc1xuKHRob3VzYW5kcyknCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAKYGBgCgoKCmBgYHtyfQpuYS5vbWl0KGRmKSAlPiUgZ2dwbG90KGFlcyh4ID0gZnJwLCB5ID0gcHJlY2lwaXRhY2FvLCBhbHBoYSA9IHJpc2NvZm9nbykpICsKICBnZW9tX3BvaW50KCkKCm5hLm9taXQoZGYpICU+JSBnZ3Bsb3QoYWVzKHggPSBsb2coZnJwKSwgeSA9IHByZWNpcGl0YWNhbywgYWxwaGEgPSByaXNjb2ZvZ28pKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKTm90aWNlIHRoZSByZWxhdGlvbnNoaXAgYW1vbmcgdGhlIGZpcmUgcmFkaWF0aXZlIHBvd2VyLCB0aGUgcHJlY2lwaXRhdGlvbiBhbmQgdGhlIHJpc2sgb2YgZmlyZS4gSXQgc2VlbXMgdGhhdCwgd2l0aCBtb3JlIHByZWNpcGl0YXRpb24sIHRoZSBmcnAgaXMgcmVkdWNlZCB3aGlsZSB3aXRoIGxlc3MgcHJlY2lwaXRhdGlvbiB0aGUgcmlzayBvZiBmaXJlIHJlbWFpbnMgbW9zdGx5CgpgYGB7cn0KbmEub21pdChkZikgJT4lIGdncGxvdChhZXMoeCA9IHJpc2NvZm9nbywgeSA9IHByZWNpcGl0YWNhbywgYWxwaGEgPSBmcnApKSArCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9ICJqaXR0ZXIiKQpgYGAKCkxvb2tpbmcgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZmlyZSByYWRpYXRpdmUgcG93ZXIgYWNyb3NzIGRpZmZlcmVudCBiaW9tYXMsIHdlIGNhbiBzZWUgdGhhdCBpdCBpcyByb3VnaGx5IHRoZSBzYW1lLCBleGNlcHQgZm9yIE1hdGEgQXRsYW50aWNhIGFuZCBQYW1wYSBtYXliZSBiZWNhdXNlIFBhbXBhIGlzIHNpdHVhdGVkIGF0IHRoZSBzb3V0aCBicmF6aWwsIHdoZXJlIHRlbXBlcmF0dXJlcyBhcmUgbG93ZXIgd2hpY2ggZGVjcmVhc2VzIHRoZSBjaGFuY2VzIG9mIGZpcmUgb2N1cnJlbmNlcy4gT24gdGhlIG90aGVyIHNpZGUsIE1hdGEgQXRsYW50aWNhIGlzIG1vc3RseSBkaXN0cmlidXRlZCBhbG9uZyB0aGUgY29hc3QsIGltcGx5aW5nIHRoYXQgdGhlIGh1bWlkaXR5IHRoYXQgY29tZXMgZnJvbSB0aGUgb2NlYW4gbWF5IGhvbGQgZmlyZSBvY3VycmVuY2VzIGZyb20gc3Ryb2tlLgoKYGBge3J9Cm5hLm9taXQoZGYpICU+JSBnZ3Bsb3QoYWVzKHggPSBiaW9tYSwgeSA9IGxvZyhmcnApKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3J9CnAgPC0gbmEub21pdChkZikgJT4lIGdncGxvdChhZXMoeCA9IGxvZyhmcnApLCBjb2wgPSBiaW9tYSwgZmlsbCA9IGJpb21hKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMSkKCmdncGxvdGx5KHApCmBgYAoKQXMgd2UgY2FuIHNlZSBiZWxvdywgd2UgY2Fubm90IHRha2UgYW55IGNvbmNsdXNpb24gYWJvdXQgdGhlIHJpc2sgb2YgZmlyZSB3aXRoIHJlc3BlY3QgdG8gdGhlIGZpcmUgcmFkaWF0aXZlIHBvd2VyLiBTbyBzYWQuCgpgYGB7cn0KbmEub21pdChkZikgJT4lIAogIGdncGxvdChhZXMoeCA9IHJpc2NvZm9nbywgeSA9IGxvZyhmcnApLCBjb2wgPSBiaW9tYSkpICsKICAgIGdlb21fcG9pbnQoKSArICMgQ29weSBmcm9tIFBsb3QgMQogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKwogICAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gMSksIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGxpbmV0eXBlID0gMikgCmBgYAoKCgpgYGB7cn0KZGF0YTE5IDwtIGRmICU+JSBmaWx0ZXIoeWVhcihkYXRhaG9yYSkgPT0gJzIwMTknKQoKZGYgJT4lIAogIHNlbGVjdChlc3RhZG8pICU+JSAKICBncm91cF9ieShlc3RhZG8pICU+JSAKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lIAogIHJpZ2h0X2pvaW4obWFwX2RmLCBieSA9ICdlc3RhZG8nKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGdyb3VwID0gY29kaWdvKSkgKyAKICAgIGdlb21fcG9seWdvbigpICsgCiAgICBnZW9tX3BvaW50KGluaGVyaXQuYWVzID0gRiwgZGF0YSA9IGRhdGExOSwgYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSwgY29sID0gYmlvbWEpLCBzaXplID0gMC4xLCBzaGFwZSA9IDMpICsKICAgIGdlb21fcGF0aChjb2xvciA9ICd3aGl0ZScsIHNpemUgPSAwLjEpCmBgYAoKYGBge3J9CmRhdGFfeWVhcmx5IDwtIGRmICU+JSBtdXRhdGUoYW5vID0geWVhcihkYXRhaG9yYSkpCgpkZiAlPiUKICBzZWxlY3QoZXN0YWRvLCBkYXRhaG9yYSkgJT4lCiAgbXV0YXRlKGFubyA9IHllYXIoZGF0YWhvcmEpKSAlPiUKICBncm91cF9ieShlc3RhZG8sIGFubykgJT4lIAogIHN1bW1hcmlzZShuID0gbigpKSAlPiUgCiAgcmlnaHRfam9pbihtYXBfZGYsIGJ5ID0gJ2VzdGFkbycpICU+JQogIGdncGxvdChhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBncm91cCA9IGNvZGlnbykpICsKICAgIGdlb21fcG9seWdvbigpICsKICAgIGdlb21fcG9pbnQoaW5oZXJpdC5hZXMgPSBGLCBkYXRhID0gZGF0YV95ZWFybHksIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGNvbCA9IGJpb21hKSwgc2l6ZSA9IDAuMDUpICsKICAgIGZhY2V0X3dyYXAofmFubykgKwogICAgZ2VvbV9wYXRoKGNvbG9yID0gJ3doaXRlJywgc2l6ZSA9IDAuMSkKIyAKIyAKYGBgCgoKCkZyb20gTmFzYVBvd2VyIEFQSSB3ZSBjYW4gb2J0YWluIHVzZWZ1bCBkYXRhIGZvciBhYm91dCAxNDYgd2VhdGhlciBhbmQgY2xpbWF0ZSBwYXJhbWV0ZXJzLCBzdWNoIGFzOgoKUmVsYXRpdmUgaHVtaWR0eSAoYXQgdHdvIG1ldGVycykKVGVtcGVyYXR1cmUgKGF0IHR3byBtZXRlcnMpClByZWNpcGl0YXRpb24gKG1tKQpNYXhpbXVtL01pbmltdW0gTW9udGhseSBEaWZmZXJlbmNlIEZyb20gTW9udGhseSBBdmVyYWdlZCBBbGwgU2t5IEluc29sYXRpb24KV2luZCBTcGVlZCAKCmdlb2dyYXBoaWNhbCBkYXRhIGFyZSBwcm92aWRlZCBhdCB0aGUgcmVzb2x1dGlvbiBvZiAxLzIgYXJjIGRlZ3JlZSBsb25naXR1ZGUgYnkgMS8yIGFyYyBkZWdyZWUgbGF0aXR1ZGUuIGZvciBtb3JlIGRldGFpbGVkIGluZm8gZ28gdG8gaHR0cHM6Ly9wb3dlci5sYXJjLm5hc2EuZ292L2RvY3VtZW50cy9QT1dFUl9EYXRhX3Y5X21ldGhvZG9sb2d5LnBkZgoKQW5vdGhlciB1c2VmdWwgcmVzb3VyY2UgY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vZWFydGhvYnNlcnZhdG9yeS5uYXNhLmdvdi9pbWFnZXMvMTQ1NDY0L2ZpcmVzLWluLWJyYXppbAoKQWxzbyBpbiBodHRwczovL3d3dy5nbG9iYWxmaXJlZGF0YS5vcmcvZGF0YS5odG1sIHdlIGNhbiBmaW5kIGRhdGEgc3VjaCBhcyBjYXJib24gZW1pc3Npb25zIGFuZCBkcnkgbWF0dGVyIGVtaXNzaW9ucy4KCmBgYHtyfQojIG5hc2FWaWlycyA8LSByZWFkLmNzdigiLi9kYXRhYmFzZS9ETF9GSVJFX1YxXzc3OTk0L2ZpcmVfYXJjaGl2ZV9WMV83Nzk5NC5jc3YiKQojIG5hc2FNb2RpcyA8LSAgcmVhZC5jc3YoIi4vZGF0YWJhc2UvRExfRklSRV9NNl83ODA2Ny9maXJlX2FyY2hpdmVfTTZfNzgwNjcuY3N2IikKIyBvYmplY3Rfc2l6ZShuYXNhTW9kaXMpCiMgb2JqZWN0X3NpemUobmFzYVZpaXJzKQojIFZpZXcobmFzYU1vZGlzKQojIFZpZXcobmFzYVZpaXJzKQpgYGAKCgoKCgoK